/*
 * Copyright (C) 2022 HiHope Open Source Organization .
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http:// www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 *
 * limitations under the License.
 */


//begin ssd1306.c

/*
 * Copyright (C) 2022 HiHope Open Source Organization .
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http:// www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 *
 * limitations under the License.
 */
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>

#include "ohos_init.h"
#include "cmsis_os2.h"
#define TWO 2
#define THIRTY 30
#define ATTR 4096

#include <stddef.h>
//#include <stdio.h>
#include "iot_gpio.h"
#include "iot_i2c.h"
#include "iot_errno.h"
#include "oled_fonts.h"
#include "oled_ssd1306.h"

/*ARRAY_SIZE(a)
{
    sizeof(a)/sizeof(a[0]);
}*/

#define OLED_I2C_IDX 0

#define OLED_WIDTH    (128)
#define OLED_I2C_ADDR 0x78 // 默认地址为 0x78
#define OLED_I2C_CMD 0x00 // 0000 0000       写命令
#define OLED_I2C_DATA 0x40 // 0100 0000(0x40) 写数据
#define OLED_I2C_BAUDRATE (400*1000) // 400k
#define TWO 2
#define ONE_HUNDRED_TWENTY 120
#define EIGHT 8
#define SIX 6
#define SIXTEEN 16
#define ONE_HUNDRED_TWENTY_EIGHT 128
#define FOUR 4
#define FOURTEEN 14
#define THIRTEEN 13


#define DELAY_100_MS (100*1000)I2cWiteByte

typedef enum{
	BANK0 = 0, //BANK0�Ĵ���
	BANK1,     //BANK1�Ĵ���
}bank_e;

#define PAJ7620_ID           0x73<<1 //�豸��ַ
#define PAJ_REGITER_BANK_SEL 0XEF    //BANKѡ��Ĵ���
#define PAJ_BANK0            0X00    //BANK0
#define PAJ_BANK1            0X01    //BANK1

//BANK0 �Ĵ�����
#define PAJ_SUSPEND_CMD            0X03 //�����豸����
#define PAJ_SET_INT_FLAG1          0X41 //�������Ƽ���жϼĴ���1
#define PAJ_SET_INT_FLAG2          0X42 //�������Ƽ���жϼĴ���2
#define PAJ_GET_INT_FLAG1          0X43 //��ȡ���Ƽ���жϱ�־�Ĵ���1(��ȡ���ƽ��)
#define PAJ_GET_INT_FLAG2          0X44 //��ȡ���Ƽ���жϱ�־�Ĵ���2(��ȡ���ƽ��)
#define PAJ_GET_STATE              0X45 //��ȡ���Ƽ�⹤��״̬
#define PAJ_SET_HIGH_THRESHOLD     0x69 //�����ͺ�߷�ֵ�����ڽӽ����ģʽ�£�
#define PAJ_SET_LOW_THRESEHOLD     0X6A //�����ͺ�ͷ�ֵ
#define PAJ_GET_APPROACH_STATE     0X6B //��ȡ�ӽ�״̬ ��1��PS data>= PS threshold ,0:PS data<= Low threshold��
#define PAJ_GET_GESTURE_DATA       0X6C //��ȡ�ӽ�����
#define PAJ_GET_OBJECT_BRIGHTNESS  0XB0 //��ȡ�����������ȣ����255��
#define PAJ_GET_OBJECT_SIZE_1      0XB1 //��ȡ���������С�Ͱ�λ��bit7:0��(���900)
#define PAJ_GET_OBJECT_SIZE_2      0XB2 //��ȡ���������С����λ��bit3:0��

//BANK1 �Ĵ�����
#define PAJ_SET_PS_GAIN         0X44 //���ü�������С (0:1x gain 1:2x gain)
#define PAJ_SET_IDLE_S1_STEP_0  0x67 //����S1����Ӧ����
#define PAJ_SET_IDLE_S1_STEP_1  0x68 
#define PAJ_SET_IDLE_S2_STEP_0  0X69 //����S2����Ӧ����
#define PAJ_SET_IDLE_S2_STEP_1  0X6A 
#define PAJ_SET_OP_TO_S1_STEP_0 0X6B //����OP��S1�Ĺ���ʱ��
#define PAJ_SET_OP_TO_S1_STEP_1 0X6C
#define PAJ_SET_S1_TO_S2_STEP_0 0X6D //����S1��S2�Ĺ���ʱ��
#define PAJ_SET_S1_TO_S2_STEP_1 0X6E
#define PAJ_OPERATION_ENABLE    0X72 //����PAJ7620U2ʹ�ܼĴ���

uint8_t I2C0Init()
{
    IoTGpioInit(THIRTEEN);
    hi_io_set_func(THIRTEEN, SIX);
    IoTGpioInit(FOURTEEN);
    hi_io_set_func(FOURTEEN, SIX);

    return IoTI2cInit(OLED_I2C_IDX, OLED_I2C_BAUDRATE);
}
typedef struct {
    /** Pointer to the buffer storing data to send */
    unsigned char *sendBuf;
    /** Length of data to send */
    unsigned int  sendLen;
    /** Pointer to the buffer for storing data to receive */
    unsigned char *receiveBuf;
    /** Length of data received */
    unsigned int  receiveLen;
} IotI2cData;

static uint32_t I2cWriteByte(uint8_t deviceAddr, uint8_t regAddr, uint8_t byte)
{
    unsigned int id = OLED_I2C_IDX;
    uint8_t buffer[] = {regAddr, byte};
    IotI2cData i2cData = {0};

    i2cData.sendBuf = buffer;
    i2cData.sendLen = sizeof(buffer)/sizeof(buffer[0]);

    return IoTI2cWrite(id, deviceAddr, i2cData.sendBuf, i2cData.sendLen);
}

uint8_t I2cReadByte(uint8_t regAddr)
{
    unsigned int id = OLED_I2C_IDX;
    uint8_t buffer=0;
    IoTI2cWrite(id,PAJ7620_ID,&regAddr,1);
    IoTI2cRead(id,PAJ7620_ID,&buffer,1);
    return buffer;
}

/**
 * @brief Write a command byte to OLED device.
 *
 * @param cmd the commnad byte to be writen.
 * @return Returns {@link IOT_SUCCESS} if the operation is successful;
 * returns an error code defined in {@link wifiiot_errno.h} otherwise.
 */
static uint32_t WriteCmd(uint8_t cmd)
{
    return I2cWriteByte(OLED_I2C_ADDR, OLED_I2C_CMD, cmd);
}

/**
 * @brief Write a data byte to OLED device.
 *
 * @param cmd the data byte to be writen.
 * @return Returns {@link IOT_SUCCESS} if the operation is successful;
 * returns an error code defined in {@link wifiiot_errno.h} otherwise.
 */
static uint32_t WriteData(uint8_t data)
{
    return I2cWriteByte(OLED_I2C_ADDR, OLED_I2C_DATA, data);
}

/**
 * @brief ssd1306 OLED Initialize.
 */
uint32_t OledInit(void)
{
    static const uint8_t initCmds[] = {
        0xAE, // --display off
        0x00, // ---set low column address
        0x10, // ---set high column address
        0x40, // --set start line address
        0xB0, // --set page address
        0x81, // contract control
        0xFF, // --128
        0xA1, // set segment remap
        0xA6, // --normal / reverse
        0xA8, // --set multiplex ratio(1 to 64)
        0x3F, // --1/32 duty
        0xC8, // Com scan direction
        0xD3, // -set display offset
        0x00, //
        0xD5, // set osc division
        0x80, //
        0xD8, // set area color mode off
        0x05, //
        0xD9, // Set Pre-Charge Period
        0xF1, //
        0xDA, // set com pin configuartion
        0x12, //
        0xDB, // set Vcomh
        0x30, //
        0x8D, // set charge pump enable
        0x14, //
        0xAF, // --turn on oled panel
    };

    

    for (size_t i = 0; i < 27; i++) {
        uint32_t status = WriteCmd(initCmds[i]);
        if (status != IOT_SUCCESS) {
            return status;
        }
    }
    return IOT_SUCCESS;
}


void OledSetPosition(uint8_t x, uint8_t y)
{
    WriteCmd(0xb0 + y);
    WriteCmd(((x & 0xf0) >> FOUR) | 0x10);
    WriteCmd(x & 0x0f);
}

/* 全屏填充 */
void OledFillScreen(uint8_t fillData)
{
    uint8_t m = 0;
    uint8_t n = 0;

    for (m=0; m < EIGHT; m++) {
        WriteCmd(0xb0 + m);
        WriteCmd(0x00);
        WriteCmd(0x10);

        for (n=0; n < ONE_HUNDRED_TWENTY_EIGHT; n++) {
            WriteData(fillData);
        }
    }
}

/**
 * @brief 8*16 typeface
 * @param x: write positon start from x axis
 * @param y: write positon start from y axis
 * @param ch: write data
 * @param font: selected font
 */
void OledShowChar(uint8_t x, uint8_t y, uint8_t ch, Font font)
{
    uint8_t c = 0;
    uint8_t i = 0;
    int num_x = x;
    int num_y = y;

    c = ch - ' ';
    if (x > OLED_WIDTH - 1) {
    num_x = 0;
    num_y = y + TWO;
    }

    if (font == FONT8x16) {
        OledSetPosition(x, y);
        for (i = 0; i < EIGHT; i++) {
            WriteData(F8X16[c*SIXTEEN + i]);
        }

        OledSetPosition(x, y+1);
        for (i = 0; i < EIGHT; i++) {
            WriteData(F8X16[c*SIXTEEN + i + EIGHT]);
        }
    } else {
        OledSetPosition(x, y);
        for (i = 0; i < SIX; i++) {
            WriteData(F6x8[c][i]);
        }
    }
}

void OledShowString(uint8_t x, uint8_t y, const char* str, Font font)
{
    uint8_t j = 0;
    int num_x = x;
    int num_y = y;
    if (str == NULL) {
    printf("param is NULL,Please check!!!\r\n");
    return;
    }

    while (str[j]) {
    OledShowChar(num_x, num_y, str[j], font);
    num_x += EIGHT;
    if (x > ONE_HUNDRED_TWENTY) {
    num_x = 0;
    num_y += TWO;
    }
    j++;
    }
}
//end 

//UART codes
#include "iot_uart.h"
#define UART1TX 6
#define UART1RX 5
void UARTInit(void)
{
	hi_io_set_func(UART1TX, 2);
    hi_io_set_func(UART1RX, 2);

	IotUartAttribute uart_attr = {0};
    uart_attr.baudRate = 9600;
    uart_attr.dataBits = IOT_UART_DATA_BIT_8;
    uart_attr.stopBits = IOT_UART_STOP_BIT_1;
    uart_attr.parity = IOT_UART_PARITY_NONE;

	IoTUartInit(1, &uart_attr);
    
}

uint8_t UARTShowString(uint8_t *str)
{
    return IoTUartWrite(1,str, 2*sizeof(str)/sizeof(str[0]));
}

void paj7620u2_selectBank(bank_e bank)
{
	switch(bank)
	{
		case BANK0: I2cWriteByte(PAJ7620_ID,PAJ_REGITER_BANK_SEL,PAJ_BANK0);break;//BANK0寄存器区域
		case BANK1: I2cWriteByte(PAJ7620_ID,PAJ_REGITER_BANK_SEL,PAJ_BANK1);break;//BANK1寄存器区域
	}
			
}

uint8_t SensorWake(void)
{
    for(uint8_t i=0;i<=4;i++)
    {
        I2cWriteByte(PAJ7620_ID,PAJ7620_ID,0);
        usleep(5000);
        I2cWriteByte(PAJ7620_ID,PAJ7620_ID,0);
        usleep(5000);
        paj7620u2_selectBank(BANK0);
        if(I2cReadByte==0x20)
        {
            UARTShowString("Sensor awaken\r\n");
            return IOT_SUCCESS;
        }
    }
    return IOT_FAILURE;
}

uint8_t SensorInit(void)
{
    if(SensorWake()==IOT_FAILURE)
        return IOT_FAILURE;
    const unsigned char init_Array[][2] = 
    {
        {0xEF,0x00},
        {0x37,0x07},
        {0x38,0x17},
        {0x39,0x06},
        {0x42,0x01},
        {0x46,0x2D},
        {0x47,0x0F},
        {0x48,0x3C},
        {0x49,0x00},
        {0x4A,0x1E},
        {0x4C,0x20},
        {0x51,0x10},
        {0x5E,0x10},
        {0x60,0x27},
        {0x80,0x42},
        {0x81,0x44},
        {0x82,0x04},
        {0x8B,0x01},
        {0x90,0x06},
        {0x95,0x0A},
        {0x96,0x0C},
        {0x97,0x05},
        {0x9A,0x14},
        {0x9C,0x3F},
        {0xA5,0x19},
        {0xCC,0x19},
        {0xCD,0x0B},
        {0xCE,0x13},
        {0xCF,0x64},
        {0xD0,0x21},
        {0xEF,0x01},
        {0x02,0x0F},
        {0x03,0x10},
        {0x04,0x02},
        {0x25,0x01},
        {0x27,0x39},
        {0x28,0x7F},
        {0x29,0x08},
        {0x3E,0xFF},
        {0x5E,0x3D},
        {0x65,0x96},
        {0x67,0x97},
        {0x69,0xCD},
        {0x6A,0x01},
        {0x6D,0x2C},
        {0x6E,0x01},
        {0x72,0x01},
        {0x73,0x35},
        {0x74,0x00},
        {0x77,0x01}
    };
    paj7620u2_selectBank(BANK0);
    for(uint8_t i=0;i<sizeof(init_Array)/2;i++)
	{
		I2cWriteByte(PAJ7620_ID, init_Array[i][0],init_Array[i][1]);//初始化PAJ7620U2
	}
    paj7620u2_selectBank(BANK0);
    return IOT_SUCCESS;
}

uint16_t SensorRead(void)
{
    uint8_t a = I2cReadByte(PAJ_GET_INT_FLAG1);
    uint8_t b = I2cReadByte(PAJ_GET_INT_FLAG1+1);
    uint16_t a16=a<<8;
    a16=a16|b;
    return a16;
}

static void OledTask(int *arg)
{
    (void)arg;
    I2C0Init();
    OledInit();
    OledFillScreen(0x00);
    UARTInit();
    sleep(1);
    if(SensorInit()==IOT_FAILURE)
    {
        OledShowString(0,2,"Sensor Failure",TWO);
        while (1)
        {
            UARTShowString("Sensor Failure.\r\n");
            sleep(2);
        }
    }
    OledShowString(0,0,"Front v2.5",TWO);
    UARTShowString("Front v2.5");
    while(1)
    {
        uint16_t gesture=SensorRead();
        if(gesture!=0)
        {
            uint8_t flag=gesture>>8;
            
            IoTUartWrite(1,&flag,1);
            switch(flag)
            {
                case 0x01:
                    OledShowString(0,2,"  Left",TWO);
                    break;
                case 0x02:
                    OledShowString(0,2," Right",TWO);
                    break;
                case 0x04:
                    OledShowString(0,2,"  Down",TWO);
                    break;
                case 0x08:
                    OledShowString(0,2,"   Up",TWO);
                    break;
                case 0x10:
                    OledShowString(0,2,"Forward",TWO);
                    break;
                case 0x20:
                    OledShowString(0,2,"Backward",TWO);
                    break;
                case 0x40:
                    OledShowString(0,2," Clock",TWO);
                    break;
                case 0x80:
                    OledShowString(0,2,"C-clock",TWO);
                    break;
                default:
                    break;
            }
            sleep(2);
            OledShowString(0,2,"        ",TWO);
        }
        usleep(10000);
    }

}

static void OledDemo(void)
{
    osThreadAttr_t attr;

    attr.name = "OledTask";
    attr.attr_bits = 0U;
    attr.cb_mem = NULL;
    attr.cb_size = 0U;
    attr.stack_mem = NULL;
    attr.stack_size = ATTR;
    attr.priority = osPriorityNormal;

    if (osThreadNew(OledTask, NULL, &attr) == NULL) {
        printf("[OledDemo] Falied to create OledTask!\n");
    }
}

APP_FEATURE_INIT(OledDemo);